var fs=require('fs');

/*****************************************************************************/
function compile_header()
{
	return [
		'data=function(globalspace,cspace) {\n',
		'	var ret=[];\n'
	];
}

/*****************************************************************************/
function compile_footer()
{
	return '	return ret.length==1?ret[0]:ret.join("");\n}\n';
}


/*****************************************************************************/
function escape_text(text)
{
	if (typeof (text)=="number") return text.toString();
	text=text.replace(/\\/g,'\\\\');
	text=text.replace(/"/g,'\\"');
	text=text.replace(/'/g,"\\'");
	text=text.replace(/\r?\n/g,'\\n');
	text=text.replace(/\r/g,'\\n');
	return text;
}

/*****************************************************************************/
function compile_text(text)
{
	return 'ret.push("'+escape_text(text)+'");\n';
}

/*****************************************************************************/
function compile_set(v1,v2)
{
	if (v2.match(/^[\d"']/)) return 'cspace.'+v1+'='+v2+';\n';
	return 'cspace.'+v1+'=cspace.'+v2+';\n';
}

/*****************************************************************************/
function compile_modifiers(modifiers)
{
	if (!modifiers.length) return ["",""];
	var head="";
	var tail="";
	var marr=modifiers.split(/:/);
	marr.pop();
	head="";
	for (var i=0,s=marr.length;i<s;i++) {
		x=marr[i];
		var p="";
		var arr;
		if (arr=x.match(/^(\w+)\((.*)\)$/)) {
			x=arr[1];
			p=(arr[2].match(/^(?:".*"|\d+)$/))?arr[2]+",":"cspace."+arr[2]+",";
		}
		head+='globalspace.F("Views","'+x+'",'+p;
		tail+=")";
	}
//	modifiers='globalspace.functions.'+marr.join('(globalspace,globalspace.functions.');
//	for (var i=0;i<marr.length;i++) tail+=")";
//	modifiers+='(globalspace,';
	return [head,tail];
}

/*****************************************************************************/
function compile_value(value,modifiers,methodargs)
{
	if (!methodargs) methodargs="";
//	return '	try {ret.push(cspace.'+value+');} catch(){}\n';
	var marr=compile_modifiers(modifiers);
//	return 'ret.push('+marr[0]+reparseArgs(value,value)+marr[1]+');\n';
	return 'ret.push('+marr[0]+'cspace.'+value+methodargs+marr[1]+');\n';
}

/*****************************************************************************/
function compile_include(value)
{
	return 'ret.push(globalspace.views.process("'+value+'",cspace));\n';
}

/*****************************************************************************/
function compile_include_plus(str,varname)
{
	return 'ret.push(globalspace.views.process("'+str+'"+cspace.'+varname+',cspace));\n';
}

/*****************************************************************************/
function compile_recurse(viewname,dst,src,store,tabs)
{
	store.push(src);
	var restore="";
	for (var i=0;i<store.length;i++) {
		restore+=tabs+"	cspace."+store[i]+"=st["+i+"];\n";
	}
	var ret=
		tabs+'{\n'+
		tabs+'	var st=[cspace.'+store.join(",cspace.")+'];\n'+
		tabs+'	cspace.'+dst+'=cspace.'+src+';\n'+
		tabs+'	ret.push(globalspace.views.process("'+viewname+'",cspace));\n'+
		restore+
		tabs+'}\n';
	return ret;
}


/*****************************************************************************/
function reparse_args(args,fname)
{
	if (!args.length) return "";
	if (args=="true" || args=="false") return args;
	if (args.match(/^[a-zA-Z][a-z_A-Z\.]*$/)) return "cspace."+args;
	if (args.match(/^(\d+|\d+\.\d*|\.\d+|".*?"|'.*?')$/)) return args;
//	args=args.replace(/\[([^\]]+)\]/)
	var arr=args.match(/^\{(.*)\}$/);
	if (!arr) throw new Error("viewcompiler::reparse_args("+fname+") - Can not parse args, args='"+args+"'\n");
	var tmp;
	var args=","+arr[1];
	tmp="{";
	var i=0;
	while (arr=args.match(/^,\s*(\w+|".*?")\s*:\s*(".*?"|\d+|\d+\.\d*|\w[\w\.]*)\s*/)) {
		args=args.replace(/^,\s*(\w+|".*?")\s*:\s*(".*?"|\d+|\d+\.\d*|\w[\w\.]*)\s*/,"");
		if (i) tmp+=",";
		tmp+=arr[1];
		tmp+=":";
		tmp+=reparse_args(arr[2],fname);
		i++;
	}
	tmp+="}";
	return tmp;
}

/*****************************************************************************/
function compile_controller_call(modifiers,fname1,fname2,args_src)
{
	var args=args_src;
	var marr=compile_modifiers(modifiers);
	if (args.length) args=","+args_src.replace(/\$\./g,"cspace.");//reparse_args(args_src,fname1+"."+fname2);
	return 'ret.push(globalspace.C("'+fname1+'","'+(fname2||"index")+'"'+(args.length?args:"")+'));\n';
//	return 'if (globalspace["controllers"]["'+fname+'"]) try{ret.push('+marr[0]+'globalspace["controllers"]["'+fname+'"](globalspace'+args+')'+marr[1]+');} catch(e) {ret.push(e);} else ret.push("[No controller '+fname+']");\n';
}

/*****************************************************************************/
function compile_foreach(var1,value)
{
	return 'if (cspace.'+value+') {var '+var1+'A=cspace.'+value+';for (var '+var1+'=0;'+var1+'<'+var1+'A.length;'+var1+'++) {if (cspace.fields) cspace.fields.foreach_i='+var1+';cspace.'+var1+'='+var1+'A['+var1+'];\n';
}

/*****************************************************************************/
function compile_for(var1,start,end)
{
	var rp1=reparse_args(start);
	var rp2=reparse_args(end);
	return '{var '+var1+','+var1+'E;if ('+rp1+'<'+rp2+') {'+var1+'='+rp1+';'+var1+'E='+rp2+';} else {'+var1+'E='+rp1+';'+var1+'='+rp2+';} while ('+var1+'<='+var1+'E) {if (cspace.fields) cspace.fields.foreach_i='+var1+';cspace.'+var1+'='+var1+';'+var1+'++;\n';
}


/*****************************************************************************/
function compile_if_unless(type,value)
{
	if (type=="if") {
		return 'if (cspace.'+value+' && cspace.'+value+'!="0") {\n';
	} else {
		return 'if (!cspace.'+value+' || cspace.'+value+'=="0") {\n';
	}
}

/*****************************************************************************/
function compile_if_eq(value1,cmprule,value2)
{
	var r='if (cspace.'+value1+cmprule;
	if (value2.match(/^[\d\"]/)) {
		r+=value2;
	} else {
		r+='cspace.'+value2;
	}
	r+=') {\n';
	return r;
}

/*****************************************************************************/
function compile_each(v1,v2)
{
	return 'if (cspace.fields && cspace.fields.foreach_i%'+v1+'=='+v2+') {\n';
}

/*****************************************************************************/
function compile_each2(varname,v1,v2,notlast)
{
	return 'if ('+varname+'%'+v1+'=='+v2+(notlast?' && '+varname+'+1!='+varname+'A.length':'')+') {\n';
}

/*****************************************************************************/
function compile_else_block(type,value)
{
	return '} else {\n';
}

/*****************************************************************************/
function compile_end_block(type,value)
{
	if (type=='foreach') return '}}\n';
	if (type=='for') return '}}\n';
	return '}\n';
}

/*****************************************************************************/
function compile_i18n_block(grp,val,easy_default)
{
	var call='globalspace.F("Views","L","'+grp.replace(/\/[^\/]+$/,"")+'","'+val+'",'+(easy_default?1:0)+')';
	return 'ret.push('+call+');\n';
}

/*****************************************************************************/
function compile(tmpl,tmplfile)
{
	var arr;
	var code=compile_header();
	var insides=[];
	var tabs="\t";
	if (tmpl==null || tmpl==undefined) {
		code.push(compile_footer());
		return code.join("");
	}
	while ((arr=tmpl.match(/^([^{]*(?:\{[^{]+)*)\{\{(.*?)\}\}/))) {
		var old=tmpl.length;
		tmpl=tmpl.replace( /^([^{]*(?:\{[^{]+)*)\{\{(.*?)\}\}/,'');
		if (old==tmpl.length) throw new Error("ERROR!!!!\n");
		if (arr[1].length) {
			code.push(tabs);
			code.push(compile_text(arr[1]));
		}
		var expr=arr[2];
//		var expr=arr[2].trim();
//		var expr=arr[2].replace(/^\s+/,"").replace(/\s+$/,"");

		var arr2;

		if (arr2=expr.match(/^!!(.+)$/)) {
			code.push(tabs);
			code.push(compile_i18n_block("generic",arr2[1]));

		} else if (arr2=expr.match(/^!(.+)$/)) {
			code.push(tabs);
			code.push(compile_i18n_block(tmplfile,arr2[1],2));
		
//		} else if (arr2=expr.match(/^!(\w+)\.(\w+)$/)) {
//			code.push(tabs);
//			code.push(compile_i18n_block(arr2[1],arr2[2]));
		
		} else if (arr2=expr.match(/^!\+(\w+)$/)) {
			code.push(tabs);
			code.push(compile_i18n_block(tmplfile,arr2[1],true));
			
		} else if (arr2=expr.match(/^else$/)) {
			// close
//			tabs=tabs.substr(0,tabs.length-1);
			code.push(tabs.substr(0,tabs.length-1));
			code.push(compile_else_block(arr2[1]));

		} else if (arr2=expr.match(/^\/(if|unless|foreach|each|set|ifeq|unlesseq|switch|each|for)$/)) {
			// close
			tabs=tabs.substr(0,tabs.length-1);
			code.push(tabs);
			code.push(compile_end_block(arr2[1]));

		} else if (arr2=expr.match(/^set ([\w\.]+)=([\w\.]+(?:|\(\)))$/)) {
			code.push(tabs);
			code.push(compile_set(arr2[1],arr2[2]));

		} else if (arr2=expr.match(/^(if|unless) (\w+(?:\.\w+(?:\(\))?|\[\d+\])*)$/)) {
			// if/unless
			code.push(tabs);
			code.push(compile_if_unless(arr2[1],arr2[2]));
			tabs+="\t";

		} else if (arr2=expr.match(/^if (\w+(?:\.\w+(?:\(\))?)*)(==|!=|<|>|<=|>=)(\d+|".*"|\w+(?:\.\w+(?:\(\))?)*)$/)) {
			// if/unless
			code.push(tabs);
			code.push(compile_if_eq(arr2[1],arr2[2],arr2[3]));
			tabs+="\t";

		} else if (arr2=expr.match(/^each (\d+):(\d+)$/)) {
			// each
			code.push(tabs);
			code.push(compile_each(arr2[1],arr2[2]));
			tabs+="\t";

		} else if (arr2=expr.match(/^each (\w+) (\d+):(\d+)$/)) {
			// each
			code.push(tabs);
			code.push(compile_each2(arr2[1],arr2[2],arr2[3],0));
			tabs+="\t";

		} else if (arr2=expr.match(/^eachnotlast (\w+) (\d+):(\d+)$/)) {
			// each
			code.push(tabs);
			code.push(compile_each2(arr2[1],arr2[2],arr2[3],1));
			tabs+="\t";

		} else if (arr2=expr.match(/^foreach (\w+) in (\w+(?:\.\w+(?:\(\))?)*)$/)) {
			// foreach
			code.push(tabs);
			code.push(compile_foreach(arr2[1],arr2[2]));
			tabs+="\t";
		
		} else if (arr2=expr.match(/^for (\w+) in (\w+(?:\.\w+(?:\(\))?)*) \.\. (\w+(?:\.\w+(?:\(\))?)*)$/)) {
			// foreach
			code.push(tabs);
			code.push(compile_for(arr2[1],arr2[2],arr2[3]));
			tabs+="\t";

		//} else if (arr2=expr.match(/^((?:\w+(?:|\((?:"[^"]*"|\d+|\w+(?:\.\w+)*)\))?:)*)(\w+(?:\.\w+(?:\(\))?|\[\w+(?:\.\w+)*\])*)$/)) {
		} else if (arr2=expr.match(/^((?:\w+(?:|\((?:"[^"]*"|\d+|\w+(?:\.\w+)*)\))?:)*)(\w+(?:\.\w+|\[\d+\])*)(\(.*\))?$/)) {
			// value
			code.push(tabs);
			code.push(compile_value(arr2[2],arr2[1],arr2[3]));
		
		} else if (arr2=expr.match(/^switch \w+(?:\.\w+)*$/)) {
			// switch
			code.push(tabs);
			throw new Error("TODO (switch)");
		
		} else if (arr2=expr.match(/^case "(.*)"$/)) {
			// case
			code.push(tabs);
			throw new Error("TODO (case)");

		} else if (arr2=expr.match(/^call\s+?((?:\w+(?:|\((?:"[^"]*"|\d+)\))?:)*)(\w+)(?:|\.(\w+))\((|(?:\w+(?:\.\w+)*|"[^"]*"|\d+)(?:,(?:\w+(?:\.\w+))*|"[^"]*"|\d+)*|\{.*?\}|\[.*?\])\)$/)) {
		//} else if (arr2=expr.match(/^(?:call |)?((?:\w+(?:|\((?:"[^"]*"|\d+)\))?:)*)(\w+)(?:|\.(\w+))\((|(?:\w+(?:\.\w+)*|"[^"]*"|\d+)(?:,(?:\w+(?:\.\w+))*|"[^"]*"|\d+)*|\{.*?\}|\[.*?\])\)$/)) {
			// function call
			code.push(tabs);
			code.push(compile_controller_call(arr2[1],arr2[2],arr2[3],arr2[4]));

		} else if (arr2=expr.match(/^include "([\w\/\-\_\.]+)"$/)) {
			// include
			code.push(tabs);
			code.push(compile_include(arr2[1]));

		} else if (arr2=expr.match(/^include "([\w\/\-\_\.]+)" plus (\w+(?:\.\w+)*)$/)) {
			// include
			code.push(tabs);
			code.push(compile_include_plus(arr2[1],arr2[2]));

		} else if (arr2=expr.match(/^recurse "([\w\/\-\_\.]+)" (\w+)=([\w\.]+), store=(\w+(?:,\w+)*)$/)) {
			// recurse
			code.push(compile_recurse(arr2[1],arr2[2],arr2[3],arr2[4].split(/,/),tabs));
		} else {
			throw new Error("viewcomplier: Unknown code:\n'"+expr+"'");
		}
	}
	if (tmpl.length) {
		code.push(tabs);
		code.push(compile_text(tmpl));
	}
	code.push(compile_footer());
	return code.join("");
}

/*****************************************************************************/
function mkdir_rec(basepath,path,is_file)
{
	var arr=path.split(/\//);
	var max=is_file?arr.length-1:arr.length;
	for (var i=0;i<max;i++) {
		basepath+=arr[i]+"/";
		var d=new fs.Directory(basepath);
		if (!d.isDirectory()) d.create();
	}
}

/*****************************************************************************/
function compile_or_load(tmpl,cachedir,tmplfile,do_not_eval,tmplfile_date) {
	var f;
	var f2;
	var code;
	var cached=false;
	if (tmplfile && cachedir) {
		f=new fs.File(cachedir+tmplfile+'.tmpl.js');
		if (f.isFile() && tmplfile_date && tmplfile_date<f.stat().mtime) {
			f.open('r');
			code=f.read().toString('utf-8');
			f.close();
			cached=true;
		}
	}
	if (!code) {
		code=compile(tmpl,tmplfile);
		if (f) {
			try {
				mkdir_rec(cachedir,tmplfile,1);
				f.open('w');
				f.write(code);
				f.close();
			} catch(e) {
			}
		}
	}
	try {
		var data;
		if (do_not_eval) data=code; else eval(code);
		return data;
	} catch (e) {
		throw new Error("COMPILED EVAL FAILED - "+e+"\n,code=\n"+code);
	}
}

/*****************************************************************************/
exports.compile_or_load=compile_or_load;

function JSON_select_recurse(space,param,values,ret,level)
{
	if (!values) return;
	for (var i=0;i<values.length;i++) {
		var value=values[i];
		ret.push({
			id: value.id,
			name: value.name,
			level: value.level
		});
//		if (value.Children) JSON_select_recurse(space,param,value.Children(),ret,level+1);
	}
}

function JSON_gridrower_recurse(space,colnames,values,ret,level)
{
	if (!values) return;
	for (var i=0;i<values.length;i++) {
		var item=values[i];
		var cells=[];
		for (var j=0;j<colnames.length;j++) cells.push(item[colnames[j]]);
		ret.push({rowClass: "item",cells: cells,id: item.id,level: item.level-1});
//		if (item.Children) JSON_gridrower_recurse(space,colnames,item.Children(),ret,level+1);
	}
}

/*****************************************************************************/
